package vip.zhenzicheng.test.juc.atomic;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 计数性能对比案例
 *
 * @author zhenzicheng
 * @date 2022-09-07 10:15
 */
@Slf4j
public class AccumulatorCompareCase {

  public static final int _1W = 10000;
  public static final int threadNumber = 50;

  /**
   * 50 个线程，每个线程 100W 次，计算总点赞数性能
   */
  public static void main(String[] args) throws InterruptedException {
    ClickNumber clickNumber = new ClickNumber();
    long startTime;
    long endTime;
    CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
    CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
    CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
    CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

    // synchronized
    startTime = System.currentTimeMillis();
    for (int i = 0; i < threadNumber; i++) {
      new Thread(() -> {
        try {
          for (int j = 0; j < 100 * _1W; j++) {
            clickNumber.clickBySynchronized();
          }
        } finally {
          countDownLatch1.countDown();
        }
      }, String.valueOf(i)).start();
    }
    countDownLatch1.await();
    endTime = System.currentTimeMillis();
    log.info("---- costTime: {} ms, by synchronized, result: {}", (endTime - startTime), clickNumber.number);

    // AtomicLong
    startTime = System.currentTimeMillis();
    for (int i = 0; i < threadNumber; i++) {
      new Thread(() -> {
        try {
          for (int j = 0; j < 100 * _1W; j++) {
            clickNumber.clickByAtomicLong();
          }
        } finally {
          countDownLatch2.countDown();
        }
      }, String.valueOf(i)).start();
    }
    countDownLatch2.await();
    endTime = System.currentTimeMillis();
    log.info("---- costTime: {} ms, by AtomicLong, result: {}", (endTime - startTime), clickNumber.atomicLong.get());

    // LongAdder
    startTime = System.currentTimeMillis();
    for (int i = 0; i < threadNumber; i++) {
      new Thread(() -> {
        try {
          for (int j = 0; j < 100 * _1W; j++) {
            clickNumber.clickByLongAdder();
          }
        } finally {
          countDownLatch3.countDown();
        }
      }, String.valueOf(i)).start();
    }
    countDownLatch3.await();
    endTime = System.currentTimeMillis();
    log.info("---- costTime: {} ms, by LongAdder, result: {}", (endTime - startTime), clickNumber.longAdder.sum());

    // LongAccumulator
    startTime = System.currentTimeMillis();
    for (int i = 0; i < threadNumber; i++) {
      new Thread(() -> {
        try {
          for (int j = 0; j < 100 * _1W; j++) {
            clickNumber.clickByLongAccumulator();
          }
        } finally {
          countDownLatch4.countDown();
        }
      }, String.valueOf(i)).start();
    }
    countDownLatch4.await();
    endTime = System.currentTimeMillis();
    log.info("---- costTime: {} ms, by LongAccumulator, result: {}", (endTime - startTime), clickNumber.longAccumulator.get());
  }
}

class ClickNumber {

  int number = 0;
  AtomicLong atomicLong = new AtomicLong(0);
  LongAdder longAdder = new LongAdder();
  LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);

  public synchronized void clickBySynchronized() { // 原生加同步锁
    number++;
  }

  public void clickByAtomicLong() { // AtomicLong
    atomicLong.getAndIncrement();
  }

  public void clickByLongAdder() { // LongAdder
    longAdder.increment();
  }

  public void clickByLongAccumulator() {
    longAccumulator.accumulate(1);
  }
}
